package tcp

import (
	"context"
	"io"
	"lnzw/engine/define"
	"lnzw/engine/log"
	"lnzw/engine/queue"
	"net"
	"sync/atomic"
	"time"

	"go.uber.org/zap"
)

type Iclient interface {
}

type ConnState int

const (
	Waiting = iota
	Prepared
)

var initId int64

// 初始化
func init() {
	initId = time.Now().UnixNano()
}

// 客户端id生成
func unitId() int64 {
	return atomic.AndInt64(&initId, 1)
}

type SocketClient struct {
	conn      net.Conn
	state     ConnState
	id        int64
	tcpsocket ITcpSocket
	sendmsgs  *queue.MPSCQueue[[]byte]
	sendchan  chan struct{}
	close     chan struct{}
	context   context.Context
}

func buildClient(conn net.Conn, tcpsocket ITcpSocket) *SocketClient {
	return &SocketClient{
		conn:      conn,
		state:     Waiting,
		id:        unitId(),
		tcpsocket: tcpsocket,
		sendmsgs:  queue.New[[]byte](),
		sendchan:  make(chan struct{}),
		close:     make(chan struct{}),
	}
}

func (c *SocketClient) GetId() int64 {
	return c.id
}

func (c *SocketClient) Attr(key define.ContextKeyId, value any) {
	c.context = context.WithValue(context.Background(), key, value)
}

func (c *SocketClient) AttrValue(key define.ContextKeyId) any {
	if c.context != nil {
		return c.context.Value(key)
	}
	log.Logger.Warn("context is nil", zap.Any("key", key))
	return nil
}

func (c *SocketClient) SendMsg(data []byte) {
	c.sendmsgs.Enqueue(data)
	if c.sendmsgs.Len() > 0 {
		c.sendchan <- struct{}{}
	}
}

func (c *SocketClient) StartWork() {
	go c.read()
	go c.write()
}

func (c *SocketClient) write() {
	for {
		select {
		case <-c.sendchan:
			for {
				data := c.sendmsgs.Dequeue()
				if data == nil {
					break
				}
				bs, err := c.tcpsocket.Encode(data)
				if err != nil {
					log.Logger.Error("encode failed", zap.Error(err))
					continue
				}
				_, err = c.conn.Write(bs)
				if err == io.EOF {
					log.Logger.Error("conn closed", zap.Error(err))
					return
				}
			}
		case <-c.close:
			c.conn.Close()
			log.Logger.Info("client closed")
			return
		}
	}
}

func (c *SocketClient) Close() {
	c.close <- struct{}{}
}

func (c *SocketClient) read() {
	defer func() {
		c.conn.Close()
		c.tcpsocket.OnClose(c)
	}()
	bufTemp := make([]byte, 0, 1024*100)
	for {
		buf := make([]byte, 512)
		n, err := c.conn.Read(buf)
		if err != nil {
			log.Logger.Error("read failed", zap.Error(err))
			return
		}
		bufTemp = append(bufTemp, buf[:n]...)
		for {
			bs, n, err := c.tcpsocket.Decode(bufTemp[:])
			if err != nil {
				break
			}
			bufTemp = bufTemp[n:]
			c.tcpsocket.OnMessage(c, bs)
		}
	}
}
